home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 4 / The 640 Meg Shareware Studio CD-ROM Volume IV (Data Express)(1994).ISO / clang / 140_01.zip / CMODEM.C < prev    next >
Text File  |  1993-06-26  |  14KB  |  611 lines

  1. /*
  2.                            CMODEM.C
  3.  
  4. Revision history: Latest revision first.
  5.  
  6. 04/Jun/82   Removed PMMI defines and ifdef for Australia,  much 
  7.             more  extensive  documentation  added,  modem  port 
  8.             defines now obtained from BDSCIO.H.  Code tidied up 
  9.             a  bit  and VARBAUD  ifdefs  added,  some  messages 
  10.             changed and numerous small changes. Bill Bolton
  11.  
  12. ??/???/81   Patched together from existing Cnet and YAM code by 
  13.             Steve Passe, Cnode SYSOP, USA.
  14.  
  15. This  program  uses  the  "Christensen" protocol  as  (used  in 
  16. YAM/MODEM7) for file transfers.
  17.  
  18. When  you execute CMODEM it signs on and immediately goes  into 
  19. the  terminal mode (without any messages) and is ready to  send 
  20. ASCII  text from your console keyboard out your modem port  and 
  21. receive ASCII text from your modem port  and display it on your 
  22. console.  An  "esc"  convention allows file  transfers  without 
  23. leaving  the program.
  24.  
  25. To send a file:
  26.         type '<esc> s <cr>',
  27. To receive one:
  28.         type '<esc> r <cr>'
  29.  
  30.     (you will be prompted for the file name in both cases)
  31.  
  32. For other commands:
  33.         type '<esc> ? <cr>'
  34.  
  35.         (<esc> is the ASCII escape character)
  36.  
  37. If you should need to send an <esc> to the remote system simply 
  38. hit  <esc>  twice  in a row for each <esc>  that  you  wish  to 
  39. send. While transfering files CMODEM echos everything, from the 
  40. file it sends or receives, to the console so be prepared to see 
  41. some  weird things when transfering object  files.  It  doesn't 
  42. send  non-ASCII  data (i.e.  control codes) to the console  but 
  43. replaces them with a full stop.
  44.  
  45. This is really just a very minimal implementation of a  program 
  46. to send and receive files in using the "Christensen" protocols. 
  47. For   a  "full  feature"  communications  program  you   should 
  48. definitely look at YAM.
  49.  
  50. Add  whatever code necessary to the initialize_port()  function 
  51. to setup your port for:
  52.     8 bits,
  53.     1 stop bit,
  54.     no parity,
  55.     baud rate of 300 if necessary,
  56.     etc.).
  57.  
  58. If  you  are  certain  that  your modem  port  will  always  be 
  59. initialised  in the correct data format before entry to  CMODEM 
  60. (perhaps initialised at cold boot time) you can just leave this 
  61. function empty.
  62.  
  63. If your modem UART has programmable baud rates you (2651,  8250 
  64. or  whatever) and you want to be able to set them  from  CMODEM 
  65. you  should  insert  code  in the baud()  function  and  define 
  66. VARBAUD.  This is probably not really worthwhile for Australian 
  67. conditions but I left the code in place in case anyone wants to 
  68. try it.  It is easier to leave your UART set at 300 baud as the 
  69. CCITT V21 modems currently available in Australia just wont run 
  70. any faster than 300 baud....maybe that will change with digital 
  71. filtering in the future.
  72.  
  73. If you are using a direct connect modem and you want to be able 
  74. to  drop the line when finished you should insert code  in  the 
  75. hangup() function.
  76.  
  77. The  defines for the modem port must be set up in the  BDSCIO.H 
  78. file  for your system.  Note that this program uses BDOS call 6 
  79. (direct  console  I/O)  for all console  access  and  therefore 
  80. should be easily usable by those with odd consoles that are not 
  81. easily defined in BDSCIO.H (such as Sorcerer or memory mapped). 
  82. If you have TELNET running you should be able to compile CMODEM 
  83. and  run  it  without any modifications (make  sure  thae  data 
  84. format as described above is correct). 
  85.  
  86. This  program  uses large chunks of the YAM  package  for  file 
  87. transfers (thanks Chuck).
  88.  
  89. */
  90.  
  91. #include "bdscio.h"
  92.  
  93. #define SOH 0x01
  94. #define EOT 0x04
  95. #define ACK 0x06
  96. #define NAK 0x15
  97. #define CAN 0x18
  98.  
  99. #define RETRYMAX 10
  100. #define TIMEOUT (-1)
  101. #define PATHLEN 20
  102. #define WCEOT (-2)
  103. #define CLKMHZ 4         /* CPU speed in Mhz */
  104.  
  105. #define CONSTAT 2
  106. #define CONIN 3
  107. #define DIR_IO 0x06
  108. #define INPUT 0xff
  109. #define NORMAL 0x1c
  110. #define ORIG 0x01
  111. #define ANSWER 0x02
  112. #define READY 0x5f
  113. #define OPT300 0x20
  114. #define OPT600 0X00
  115. #define CLEAR 0x3f
  116. #define FLAG char
  117.  
  118. int Baud;
  119. FLAG Tfile, Rfile;
  120. char Tname[PATHLEN], Rname[PATHLEN];
  121. unsigned T1pause, Timeout;
  122. char File_buf[BUFSIZ];
  123. char Checksum, Lastrx;
  124. int Wcj, Firstch;
  125.  
  126. main()
  127. {
  128.     char received, to_send, in_modem(), getch(), escflag;
  129.  
  130.     Tfile = Rfile = FALSE;
  131.     T1pause = 311*CLKMHZ;
  132.     escflag = NULL;
  133.     Baud = 300;
  134.  
  135.     initialize_port();
  136.  
  137.     printf("\n\CMODEM a 'Christensen' protocol file transfer program\n\n");
  138.  
  139.     while (TRUE) {
  140.         if (received = in_modem()) {
  141.              putch(received);
  142.         }
  143.         else if (to_send = getch()) {
  144.             if (to_send == ESC) {
  145.                 if (escflag) {
  146.                     escflag = NULL;
  147.                     out_modem(to_send);
  148.                 }
  149.                 else escflag = TRUE;
  150.             }
  151.             else {
  152.                 if (escflag) {
  153.                     escflag = NULL;
  154.                     commands(to_send);
  155.                 }
  156.                 else out_modem(to_send);
  157.             }
  158.         }
  159.     }
  160. }
  161.  
  162. char        /* get incoming byte from modem */
  163. in_modem()
  164. {
  165.     if (inp(MSTAT) & MIMASK) {    /* status & char ready bit */
  166.         return inp(MDATA);    /* ok, get the char */
  167.     }            /* or */
  168.     else return FALSE;    /* return empty */
  169. }
  170.  
  171. out_modem(out_char)    /* send byte to modem */
  172. char out_char;
  173. {
  174.     while (!(inp(MSTAT) & MOMASK))
  175.         ;                                /* wait */
  176.     outp(MDATA, out_char);    /* finally, send it */
  177. }
  178.  
  179. char        /* keyboard 'hook' and 'filter' */
  180. getch()
  181. {
  182.     return bdos(DIR_IO, INPUT);
  183. }
  184.  
  185. commands(cmd)
  186. char cmd;
  187. {
  188.     int baudrate;
  189.  
  190.     switch (tolower(cmd)) {
  191. #ifdef VARBAUD
  192.     case 'b':
  193.         printf("\n\n\tbaudrate: ");
  194.         scanf("%d", &baudrate);
  195.         baud(baudrate);
  196.         break;
  197. #endif
  198.     case 'q':
  199.         hangup();
  200.         break;
  201.     case 'r':
  202.         printf("\n\n\treceive: ");
  203.         scanf("%s", Rname);
  204.         download(Rname);
  205.         break;
  206.     case 's':
  207.         printf("\n\n\tsend: ");
  208.         scanf("%s", Tname);
  209.         upload(Tname);
  210.         break;
  211.     case '?':
  212.     case 'h':
  213.         help();
  214.         break;
  215.     default:
  216.         printf("\nbad command: '%c'\n", cmd);
  217.     }
  218. }
  219.  
  220. /*
  221.  
  222.     Most of the following functions taken from YAM package by
  223. Chuck Forsberg, BDS "C" UG disk Utilities #, some modifications made for
  224. compatibility with cnode code.
  225.  
  226. */
  227.  
  228. upload(filename)
  229. char *filename;
  230. {
  231.     int err_flag;
  232.  
  233.     if(opentx(Tname)==ERROR) {
  234.         printf("\ncan't open '%s'\n", filename);
  235.         return ERROR;
  236.     }
  237.     printf("\n'%s' open for transmission\n", Tname);
  238.     if(wctx()==ERROR) {
  239.         abort('t');
  240.     }
  241.     return OK;
  242. }
  243.  
  244.  
  245.  
  246. wctx()
  247. {
  248.     int sectnum, attempts;
  249.     char txbuf[SECSIZ];
  250.  
  251.     printf("Awaiting initial NAK - ");
  252.     while((Firstch=readbyt(400))!=NAK && Firstch!=TIMEOUT && Firstch!=CAN)
  253.     {
  254.         printf("\n\tgot 0x%2x, not NAK", Firstch);
  255.                     /* let user see it if strange char */
  256.     }
  257.     if(Firstch==CAN)
  258.         return ERROR;
  259.     if (Firstch == TIMEOUT) {
  260.         printf("\ntimeout on initial NAK!");
  261.         return ERROR;
  262.     }
  263.     sectnum=1;
  264.     while(filbuf(txbuf, SECSIZ)) {
  265.         if(wcputsec(txbuf, sectnum)==ERROR) {
  266.             return ERROR;
  267.         }
  268.         else {
  269.             sectnum++;
  270.         }
  271.     }
  272.     closetx();
  273.     attempts=0;
  274.     do {
  275.         out_modem(EOT);
  276.         purgeline();        /* why? */
  277.         attempts++;
  278.     }
  279.         while((Firstch=(readbyt(100)) != ACK) && attempts < RETRYMAX)
  280.             ;        /* wait for ACK */
  281.     if(attempts == RETRYMAX) {
  282.         printf("\nNo ACK on EOT; Aborting... ");
  283.         return ERROR;
  284.     }
  285.     else
  286.         return OK;
  287. }
  288.  
  289. wcputsec(txbuf, sectnum)
  290. char *txbuf;
  291. int sectnum;
  292. {
  293.     char attempts, *cp, xbyt;
  294.     
  295.     Firstch=0;    /* part of logic to detect CAN CAN */
  296.  
  297.     for(attempts=0; attempts <= RETRYMAX; attempts++) {
  298.         Lastrx= Firstch;
  299.         out_modem(SOH);
  300.         out_modem(sectnum);
  301.         out_modem(~sectnum);
  302.         Checksum=0;
  303.         for(Wcj=SECSIZ,cp=txbuf; --Wcj>=0; ) {
  304.             out_modem(*cp);
  305.             isprint(*cp) ? putchar(*cp) : putchar('.');
  306.             Checksum += *cp++;
  307.         }
  308.         out_modem(Checksum);
  309.         purgeline();
  310.  
  311.         Firstch=readbyt(100);
  312.         if(Firstch==CAN && Lastrx==CAN) {
  313. cancan:
  314.             printf("\nReceiver CANcelled transmission ");
  315.             return ERROR;
  316.         }
  317.         else if(Firstch==ACK)
  318.             return OK;
  319.         else if(Firstch==TIMEOUT)
  320.             printf("\nTimeout on sector ack attempt %d", attempts);
  321.         else {
  322.             printf("\nGot %2x for sector ACK attempt %d", Firstch, attempts);
  323.             for(;;) {
  324.                 Lastrx=Firstch;
  325.                 if((Firstch=readbyt(1))==TIMEOUT)
  326.                     break;
  327.                 if(Firstch==CAN && Lastrx==CAN)
  328.                     goto cancan;
  329.             }
  330.         }
  331.     }
  332.     printf("\nNo ACK on sector; Abort ");
  333.     return ERROR;
  334. }
  335.  
  336. opentx(name)
  337. char *name;
  338. {
  339.     printf("'%s' ", name);
  340.     if(fopen(name, File_buf)==ERROR){
  341.         return ERROR;
  342.     }
  343.     Tfile= TRUE;
  344.     return OK;
  345. }
  346.  
  347. /* fill buf with count chars padding with ^Z for CPM */
  348.  
  349. filbuf(buf, count)
  350. char *buf;
  351. int count;
  352. {
  353.     int c, m;
  354.     m=count;
  355.     while((c=getc(File_buf))!=EOF) {
  356.         *buf++ =c;
  357.         if(--m == 0)
  358.             break;
  359.     }
  360.     if(m==count)
  361.         return 0;
  362.     else        /* won't pad properly under cp/m because of */
  363.             /* two different EOF's (EOF & CPMEOF), and the */
  364.             /* fact that cp/m doesn't know type of file */
  365.             /* instinctively, fix later */
  366.         while(--m>=0)
  367.             *buf++ = 0x1a;        /* control z */
  368.     return count;
  369. }
  370.  
  371.  
  372. download(filename)
  373. char *filename;
  374. {
  375.     printf("ready to receive '%s'\n", Rname);
  376.     if(wcrx(Rname)==ERROR) {
  377.         abort('r');
  378.     }
  379.     return OK;
  380. }
  381.  
  382. /*
  383.  * Adapted from CMODEM13.C, written by
  384.  * Jack M. Wierda and Roderick W. Hart
  385.  */
  386.  
  387. wcrx(name)
  388. char *name;
  389. {
  390.     int sectnum, sectcurr, sectcomp;
  391.     char *cp, rxbuf[128], sendchar;
  392.  
  393.     if(openrx(name)==ERROR)
  394.         return ERROR;
  395.     sectnum=0;
  396.     sendchar=NAK;
  397.  
  398.     for(;;) {
  399.         out_modem(sendchar);    /* send it now, we're ready! */
  400.         sectcurr=wcgetsec(rxbuf, (sectnum & 0x7f) ? 50 : 130);
  401.         if(sectcurr==(sectnum+1 & 0xff)) {    /* mask low byte */
  402.  
  403.             sectnum++;
  404.             for(cp=rxbuf,Wcj=128; --Wcj>=0; )
  405.                 if(putc(*cp++, File_buf)==ERROR) {
  406.                     printf("\nDisk Full\n");
  407.                     return ERROR;
  408.                 }
  409.             sendchar=ACK;
  410.         }
  411.         else if(sectcurr==sectnum) {
  412.             printf("\nReceived dup Sector %d",sectcurr);
  413.             sendchar=ACK;
  414.         }
  415.         else if(sectcurr==WCEOT) {
  416.             out_modem(ACK);
  417.             /* don't pad the file any more than it already is */
  418.             closerx(FALSE);
  419.             return OK;
  420.         }
  421.         else {
  422.             printf(" Sync Error\n");
  423.             return ERROR;
  424.         }
  425.     }
  426. }
  427.  
  428. /*
  429.  * wcgetsec fetches a Ward Christensen type sector.
  430.  * Returns sector number encountered or ERROR if valid sector not received,
  431.  * or CAN CAN received
  432.  * or WCEOT if eot sector
  433.  * time is timeout for first char, set to 4 seconds thereafter
  434.  ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
  435.  *    (Caller must do that when he is good and ready to get next sector)
  436.  */
  437.  
  438. wcgetsec(rxbuf, time)
  439. char *rxbuf;
  440. int time;
  441. {
  442.     int sectcurr,errors;
  443.     char *cp;
  444.  
  445.     for(Lastrx=errors=0; errors<RETRYMAX; errors++) {
  446.         do {
  447.             Firstch=readbyt(time);
  448.         }
  449.         while(Firstch != SOH && Firstch != TIMEOUT && Firstch != EOT
  450.               && Firstch != CAN);    /* wait for one of these */
  451.         if(Firstch==SOH) {
  452.             sectcurr=readbyt(1);
  453.             if((sectcurr+readbyt(1))==255) {
  454.                 Checksum=0;
  455.                 for(cp=rxbuf,Wcj=128; --Wcj>=0; ) {
  456.                     isprint(*cp = readbyt(1)) ? putchar(*cp) : putchar('.');
  457.                     Checksum += (*cp++);
  458.                 }
  459.                 if(((Checksum-readbyt(1))& 0xff)==0)
  460.                     return sectcurr;
  461.                 else
  462.                     printf("Checksum Error #%d", errors);
  463.             }
  464.             else
  465.                 printf("Sector number garbled #%d", errors);
  466.         }
  467.         else if(Firstch==EOT)
  468.             return WCEOT;
  469.         else if(Firstch==CAN) {
  470.             if(Lastrx==CAN) {
  471.                 printf("\nSender CANcelled");
  472.                 return ERROR;
  473.             } else {
  474.                 Lastrx=CAN;
  475.                 continue;
  476.             }
  477.         }
  478.         else if(Firstch==TIMEOUT)
  479.             printf("\nSOH Timeout #%d", errors);
  480.  
  481.         Lastrx=0;
  482.         while(readbyt(1)!=TIMEOUT)
  483.             ;
  484.         out_modem(NAK);
  485.         time=40;
  486.     }
  487.     /* try to stop the bubble machine. */
  488.     out_modem(CAN);out_modem(CAN);out_modem(CAN);
  489.     return ERROR;
  490. }
  491.  
  492. openrx(name)
  493. char *name;
  494. {
  495.     printf("\nsaving it as '%s'", name);    /* show the name right away */
  496.     if(fopen(name, File_buf) != ERROR) {
  497.         fclose(File_buf);
  498.         printf("\nI already have one, try another name");
  499.         return ERROR;
  500.     }
  501.     if(fcreat(name, File_buf)==ERROR){
  502.         printf("\nCan't create '%s'", name);
  503.         return ERROR;
  504.     }
  505.     Rfile= TRUE;
  506.     return OK;
  507. }
  508.  
  509. readbyt(decisecs)
  510. int decisecs;
  511. {
  512.     if (inp(MSTAT) & MIMASK)
  513.         return inp(MDATA);
  514.     while (--decisecs >= 0) {
  515.         if (inp(MSTAT) & MIMASK)
  516.             return inp(MDATA);
  517.         if (bdos(DIR_IO, INPUT))    /* local forced timeout */
  518.             return TIMEOUT;
  519.         if (inp(MSTAT) & MIMASK)
  520.             return inp(MDATA);
  521.         for (Timeout = T1pause; --Timeout; )
  522.             if (inp(MSTAT) & MIMASK)
  523.                 return inp(MDATA);
  524.     }
  525.     return TIMEOUT;
  526. }
  527.  
  528. abort(flag)
  529. char flag;
  530. {
  531.     flag == 't' ? closetx() : closerx();
  532.     out_modem(CAN);out_modem(CAN);out_modem(CAN);
  533.     return ERROR;
  534. }
  535.  
  536. closetx()
  537. {
  538.     if(Tfile) {
  539.         fclose(File_buf);
  540.         printf("\n\n%s closed", Tname);
  541.         Tfile=FALSE;
  542.     }
  543. }
  544.  
  545. /* for the upld overlay command */
  546.  
  547. closerx()
  548. {
  549.     if(Rfile) {
  550.         fflush(File_buf);
  551.         fclose(File_buf);
  552.         printf("\n\n%s closed", Rname);
  553.         Rfile=FALSE;
  554.     }
  555. }
  556. purgeline()
  557. {
  558.     while (inp(MSTAT) & MIMASK)
  559.         inp(MDATA);
  560. }
  561.  
  562. /* end of code from yam */
  563.  
  564. help()
  565. {
  566.     printf("\n\tCommands:");
  567. #ifdef VARBAUD
  568.     printf("\n\t\t\t'b'aud <baudrate>");
  569. #endif
  570.     printf("\n\t\t\t'r'eceive <filename>");
  571.     printf("\n\t\t\t's'end <filename>");
  572.     printf("\n\t\t\t'q'uit");
  573.     printf("\n");
  574. }
  575.  
  576. initialize_port()
  577. {
  578. /* put code to init your modem port here */
  579. /*    this is for initializing an 8251 uart...
  580.     outp(MSTAT,0xaa);    /* insure next port access of 'command' type */
  581.     outp(MSTAT,0x40);    /* 'command' a reset */
  582.     outp(MSTAT,0x8e);    /* set the 'mode', 1-stop bit, sync 16x */
  583.             /*    eight bits, no parity */
  584.     outp(MSTAT,0x27);    /* trans, DTR, rec, RTS */
  585. */
  586. }
  587.  
  588. isprint(c)
  589. char c;
  590. {
  591.     return (c>=' ' && c <= '~') || (c == 0x0d) || (c == 0x0a) || (c==0x09);
  592. }
  593.  
  594. hangup()
  595. {
  596. /* insert code to hang up line (using CCITT 108)
  597.    if you have a direct connect modem */
  598.  
  599.     printf("\nhanging up...\n");
  600.     exit();
  601. }
  602.  
  603. #ifdef VARBAUD
  604. baud(baudrate)
  605. int baudrate;
  606. {
  607.     /* Insert code to set the baud rate of your UART here */
  608.  
  609. }
  610. #endif
  611.